;;; ----------------------------------------------------------------------
;;; concept code for controlling an (8-bit mode) IDE interface
;;; through a 6522 VIA.
;;; --
;;; Chris Baird,, <cjb@brushtail.apana.org.au>


;;; directory record format:
;;; 00-1A name, null-terminated
;;; 1B,1C start address
;;; 1D,1E end address
;;; 1F          00=empty/deleted
;;;             FF=end of directory  (..optional)
;;;             01=flat file
;;;             02- ...
;;; file location segment determined from dirindex

;;; ca65 -l iderom.asm && ld65 -t none iderom.o && scp a.out root@otter:
;;; ssh root@otter "~/vic.andmodem 40960 a.out"

            .pc02               ; desperate to get the smalldir code to fit

;            EPROM               = 1

;;; ----------------------------------------------------------------------
;;; ATA IDE drive stuff

	    IDE_REG_data        = 0
	    IDE_REG_error       = 1 ; when reading
	    IDE_REG_count       = 2 ; number of sectors to read/write
	    IDE_REG_LBA0        = 3
	    IDE_REG_LBA8        = 4
	    IDE_REG_LBA16       = 5
	    IDE_REG_LBA24       = 6 ; b4 = drive#=0, b6=LBA=1, b5+7=1
	    IDE_REG_sector      = 3
	    IDE_REG_cylinderlo  = 4
	    IDE_REG_cylinderhi  = 5
	    IDE_REG_head        = 6 ; b4 = drive#, b6=LBA=0
	    IDE_REG_status      = 7 ; when reading
	    IDE_REG_command     = 7 ; when writing

	    IDE_head_drive      = 16 ; various bit fields
	    IDE_head_LBA        = 64
	    IDE_head_extras     = 128 + 32 ; required MFM codes

	    IDE_status_Busy     = 128
	    IDE_status_Ready    = 64
            IDE_status_WrFault  = 32
            IDE_status_Seeked   = 16
	    IDE_status_DataReq  = 8
            IDE_status_CorrData = 4
            IDE_status_IDX      = 2
	    IDE_status_Err      = 1

	    IDE_DCR_SRST                = $04

	    IDE_CMD_Recalibrate         = $10
	    IDE_CMD_ReadSector          = $20
	    IDE_CMD_WriteSector         = $30
	    IDE_CMD_Seek                = $70
	    IDE_CMD_InitDriveParams     = $91
	    IDE_CMD_Idle                = $95
	    IDE_CMD_IdentifyDrive       = $EC
	    IDE_CMD_SetFeatures         = $EF

;;; ----------------------------------------------------------------------

	    VIA2 	= $9800
	    PORTB	= VIA2  ; PORTB = to IDE D0-D7
	    DDRB	= VIA2 + 2
	    PORTA 	= VIA2 + 15 ; PORTA = to IDE control lines
	    DDRA 	= VIA2 + 3

	    b_RD	= %00001000 ; IDE interface wiring
	    b_WR	= %00010000
	    b_CS0	= %00100000
            b_RST       = %01000000

            CHROUT      = $FFD2
            CHRIN       = $FFCF
            STOP        = $FFE1
            CURSORPOS   = 211

            ;;

            commandkey  = 165 ; I wonder how interrupt-safe these are..
            hasname     = 150
            hasaddr     = 155
            stringstart = 166
            stringend   = 167
            ADDR1       = 168 ; .word
            ADDR2       = 170 ; .word .. and keep it adjacent to ADDR1
            IDETEMP     = 176
            FTYPETMP    = 177
            BUFPTR      = 178   ; .word

            IDE_LBA0    = 828
            IDE_LBA8    = 829
            IDE_LBA16   = 830
            IDE_LBA24   = 831
            SECTORSIZE  = 832
            CURDIRBLK   = 833
            DIRINDEX    = 834   ; .word

            LINPUT      = 836
            DIRBUFFER   = 896   ; $380

	    FILE_NAME   = DIRBUFFER
            FILE_ADDR_L = DIRBUFFER+27
            FILE_ADDR_H = DIRBUFFER+28
            FILE_LEN_L  = DIRBUFFER+29
            FILE_LEN_H  = DIRBUFFER+30
            FILE_TYPE   = DIRBUFFER+31
            ;; probably should've included an 'exec jmp' address field..

;;; ----------------------------------------------------------------------

            .ifdef EPROM
            PROMPTCHAR = 46
            .org $9C00
            .endif

;;;

            .ifndef EPROM
            PROMPTCHAR = 161
            BIGDIRLIST = 1
	    .org 40960
            jmp CLIMAIN
            .endif

;;; ----------------------------------------------------------------------
;;; ----------------------------------------------------------------------

Disk_Init:  ldx #$FF            ; port A is all outputs
	    stx DDRA            ; B is taken care of later
            stx CURDIRBLK       ; scratch directory cache
            stz IDE_LBA0
            stz IDE_LBA8
            stz IDE_LBA16
            stz IDE_LBA24

	    lda #b_WR + b_RD + b_CS0 ; reset the drive as well
	    sta PORTA

	    lda #b_WR + b_RD + b_CS0 + b_RST
            sta PORTA
            lda #IDE_CMD_InitDriveParams
            bne poing

;;; register to read in X, value returned in A

IDE_Read_Register:
	    stz DDRB		; set PORTB for input

	    ;; /CS0=0; /RD=1; /WR=1, A0:2
	    txa
	    ora #(b_RD + b_WR + b_RST) ; activate /CS0
	    sta PORTA

	    and #~b_RD		; activate /RD
	    sta PORTA

	    ldx PORTB

	    ora #b_RD
	    sta PORTA		; deactivate /RD

	    ora #b_CS0		; deactive /CS0
	    sta PORTA
	    txa

            rts

;;;

            .ifdef EPROM
            .byte 0
CLI_IN:     jmp CLIMAIN         ; sys40000
            .if (CLI_IN - 40000)
            .warning "***"
            .warning "*** CLI_IN not 40000"
            .endif
            .endif

poing:      ldx #IDE_REG_command
            bne IDE_Write_Register
;;;

IDE_Send_LBA:
            jsr IDE_WaitGood

            lda IDE_LBA0
            ldx #IDE_REG_LBA0
            jsr IDE_Write_Register

            lda IDE_LBA8
            ldx #IDE_REG_LBA8
            jsr IDE_Write_Register

            lda IDE_LBA16
            ldx #IDE_REG_LBA16
            jsr IDE_Write_Register

            lda IDE_LBA24
            and #15
            ora #IDE_head_LBA + IDE_head_extras
            ldx #IDE_REG_LBA24
            jsr IDE_Write_Register

            lda #1              ; one sector at a time..
            ldx #IDE_REG_count
            ;jmp IDE_Write_Register


;;; register to write in X, value taken from A

IDE_Write_Register:
	    pha
	    lda #$FF		; set PORTB for output
	    sta DDRB

	    ;; /CS0=0; /RD=1; /WR=1; /RESET=1; A0:2
	    txa
	    ora #(b_RD + b_WR + b_RST) ; activate CS0
	    sta PORTA

	    tax
	    pla
	    sta PORTB           ; (does it matter if /CS0 is first?)
	    txa

	    and #~b_WR		; activate WR
	    sta PORTA

	    ora #b_WR		; deactivate WR
	    sta PORTA

	    ora #b_CS0		; deactivate CS0
	    sta PORTA

	    rts

;;;

ReadSector: jsr IDE_Send_LBA

	    lda #IDE_CMD_ReadSector
	    ldx #IDE_REG_command
	    jsr IDE_Write_Register

	    jsr IDE_WaitReady
	    jsr IDE_WaitDataReq

	    ;jsr IDE_CheckError	; check error flags
	    ;bcs PANIC          ; :/

	    ldy #0
rs1:        ldx #IDE_REG_data
	    jsr IDE_Read_Register
            cpy SECTORSIZE
            beq rs3
            bcs rs2
rs3:        sta (BUFPTR),y
rs2:        iny
	    bne rs1
	    ;jmp IDE_WaitGood

;;; these routines are probably a good place to do sanity/error checking..
;;; involving timeouts most likely.

IDE_WaitGood:
	    jsr IDE_WaitnotBusy

            ;; wait-to-go-high

IDE_WaitReady:
	    lda #IDE_status_Ready
	    .byte $2c
IDE_WaitDataReq:
	    lda #IDE_status_DataReq
            .byte $2c
IDE_CheckError:
            lda #IDE_status_Err
	    sta IDETEMP
I_WSH1:     ldx #IDE_REG_status
	    jsr IDE_Read_Register
	    bit IDETEMP
	    beq I_WSH1		; TODO delay and timeout code here
 	    rts

            ;; wait-to-go-low

IDE_WaitnotBusy:
            lda #IDE_status_Busy
	    sta IDETEMP
I_WSH2:     ldx #IDE_REG_status
	    jsr IDE_Read_Register
	    bit IDETEMP
	    bne I_WSH2		; TODO delay and timeout code here
            ;; is carry clear?
 	    rts

;;;

WriteSector:
	    jsr IDE_Send_LBA
	    lda #IDE_CMD_WriteSector
	    ldx #IDE_REG_command
	    jsr IDE_Write_Register
	    jsr IDE_WaitnotBusy
	    jsr IDE_WaitDataReq

	    ldy #0
ws1:        lda (BUFPTR),y
	    ldx #IDE_REG_data
	    jsr IDE_Write_Register
	    iny
	    bne ws1
            beq IDE_WaitGood


;;; ----------------------------------------------------------------------
;;; ----------------------------------------------------------------------
;;; The directory exists on blocks 0-255, so LBA8 should always end up as 0

BumpDirIndex:
            inc DIRINDEX
            bne bdi1
            inc DIRINDEX+1
bdi1:       rts

;;;

ResetDirScan:
            ldy #1
            sty DIRINDEX
            dey
            sty DIRINDEX+1
            dey                 ; attempt to force a refetch
            sty CURDIRBLK
            ;jmp ReadDirectory

;;;

ReadDirectory:
            lda #127
            sta SECTORSIZE
            jsr SetDirBufptr
            jsr CalcDirBlk
            cmp CURDIRBLK
            beq CalcDirOff
            sta CURDIRBLK
            jsr ReadSector      ; should really catch Carry flag signals..

CalcDirOff: lda DIRINDEX
	    lsr a
            ror a
            ror a
            ror a
            and #96
            tax
            tay
            rts

;;;

UpdateDirectory:
            jsr CalcDirBlk
            jsr SetDirBufptr
            bne WriteSector

;;;

CalcDirBlk: stz IDE_LBA16
            lda DIRINDEX+1
            sta IDE_LBA8
            lda DIRINDEX
            lsr IDE_LBA8
            ror a
            lsr IDE_LBA8
            ror a
            sta IDE_LBA0
            rts

;;;

SetDirBufptr:
            lda #<DIRBUFFER
            sta BUFPTR
            lda #>DIRBUFFER
            sta BUFPTR+1
            rts


;;; ----------------------------------------------------------------------
;;; Carry set if failed

FindName:   lda #1
            .byte $2c
FindEmpty:  lda #0
            sta FTYPETMP
            ;;

            jsr ResetDirScan
findloop:   jsr ReadDirectory
            lda FILE_TYPE,x
            cmp #255
            beq findend
            cmp FTYPETMP
            bcc findnext        ; hopefully gets 01 and 02
            cmp #0              ; looking for a deleted/null record
            beq findfound       ; exit now if =0 (and accept >1)

            ;; compare filename w/ wildcarding

            ldy stringstart
findloop1:  lda LINPUT,y
            cmp #'*'
            beq findfound        ; ..but needs carry cleared
            cmp #'?'
            beq findbr1
            cmp FILE_NAME,x
            bne findnext

findbr1:    inx
            iny
            lda FILE_NAME,x
            bne findloop1
            dey
            cpy stringend       ; check lengths the same..
            bne findnext

findfound:  jsr ReadDirectory   ; restore ReadDirectory's X&Y info
            clc
            bcc findend

findnext:   jsr BumpDirIndex
            bne findloop

;;;

SeekSegment:;; prepare the HDD for accessing the segment
            ;; LBA = 256 * dirindex
            stz IDE_LBA0
            lda DIRINDEX
            sta IDE_LBA8
            lda DIRINDEX+1
            sta IDE_LBA16

findend:    rts


;;; ----------------------------------------------------------------------

;;; 1kB EPROM inbuilt commands
;;;
;;; .L "name[" saddr]           Load
;;; ."name["] (no commandchar)  Load and $C533/$C659/$C7AE (RUN)
;;; .S "name[" saddr eaddr]     Save
;;; .$                          "Minimal" Directory listing
;;; .G addr                     Go/Execute machine code
;;; .P [n]                      Load and JMP n'th program on disk
;;; .- "name["]                 Delete
;;; .R                          Cold Reset
;;; .X                          Exit to BASIC/Break

;;; Extended command set (when built for RAM)
;;;
;;; .$                          "Full details" Directory listing
;;; .Q                          Jump back to EPROM
;;; .? "name["] or nnnn         Verbose directory record print of file/index,
;;;                             (and copy record into dirbuffer)
;;; .N "name"                   Change name in dirbuffer
;;; .T nn                       Set dirbuf filetype (0=deleted,1=file,FF=EOD)
;;; .I addr [len]               Change SADDR, FILE_LEN in dirbuffer
;;; .J nn "name                 'Juggle' dirrecord for name and index nn
;;;                             (check that T=FF isn't involved)
;;; .Z                          Listing of deleted & EOD files

;;; Maybe commands
;;;
;;; .& "name"                   &Modem download
;;; .U "name"                   &Modem upload
;;; .U [nnnn]                   Set 'User' (aka IDE_LBA24)
;;; .V "name[" saddr len]       Verify
;;; .H                          Usage summary
;;; .addr addr                  Memory dump
;;; .:addr nn                   Single byte poke

;;; Getting a directory..
;;; .P        Autoexecs the first file on disk (RAM version of 6522IDE)
;;; .$        ..which will trash the program currently in memory..
;;; .Q        ..but V2 BASIC users have lived with that for 40 years :)

;;; How to rename a file..
;;; .? "TEST001"
;;; .N "TEST.BAK"

;;; Moving the EOD marker..
;;; .? 007F
;;; .T 00
;;; .? 03FF             now handles 1024 files, but directory searches
;;; .T FF               are slower

;;; Making a machine code program a 'P' command
;;; .S "SUPERMON A0" A000 AFFF
;;; .J 02 "SUPERMON A0"
;;; .P2

;;; 00 = deleted
;;; 01 = plain file with loading address and length
;;; 02 = 'block' file that uses all 64kB (i.e FORTH block files)
;;; FF = deleted

;;; WKOY !#%'()=+@*;[]/

;;; ----------------------------------------------------------------------

CLIMAIN:    jsr Disk_Init

main0:      jsr CROUT
main:       lda #PROMPTCHAR
main2:      jsr CHROUT
            cmp #PROMPTCHAR
            bne main0

            ldy #4              ; defaults for load/save/?go
mainl1:     lda 42,y
            sta ADDR1-1,y
            dey
            bne mainl1

mainrd:     jsr CHRIN
            sta LINPUT,y
            ;; should probably catch Y overflowing LINPUT
            iny
            cmp #13
            bne mainrd

            ldy #0
            sty stringend
            sty stringstart
            sty commandkey
            sty hasaddr
            sty hasname
            dey

            ;;

parse:      iny
            lda LINPUT,y
            cmp #13
            beq exec
            cmp #33
            bcc parse
            cmp #','
            beq parse
            cmp #34
            beq gotquote

            ldx hasname         ; loop if quoting
            cpx #2
            bcs parse1
            cpx #0
            bne parse

parse1:     eor #48
            cmp #10
            bcc adddigit

            adc #$88
            cmp #$fa
            bcc nothex

adddigit:   inc hasaddr
            asl
            asl
            asl
            asl
            ldx #4
hexshift:   asl
            rol ADDR1
            rol ADDR1+1
            rol ADDR2
            rol ADDR2+1
            dex
            bne hexshift
            beq parse

nothex:     lda LINPUT,y
            sta commandkey
            bne parse

gotquote:   ldx stringend
            stx stringstart
            sty stringend
            inc hasname
            bne parse

            ;;

exec:       jsr CHROUT          ; this'll be a CR
            cpy #1              ; empty line?
            bcs execb0          ; because main is 132 bytes away..
            jmp main

execb0:     ldx hasname         ; check if quote not closed
            cpx #1
            bne execb1
            ldx stringend
            stx stringstart
            sty stringend

execb1:     ldx stringstart
            cpx stringend       ; check if empty string
            beq execb2

            dec stringend
            bmi errnullstr      ; if no quote at all
            inc stringstart

            ;; ----------------------------------------

execb2:     lda commandkey
            ;; command not in the EPROM
            .ifndef EPROM
            cmp #'Q'
            bne execs3
            jmp 40000
execs3:     cmp #'?'
            bne execs4
            jmp query
execs4:     cmp #'N'
            bne execs5
            jmp rename
execs5:     cmp #'T'
            bne execs7
            jmp changetype
execs7:     cmp #'I'
            bne execs8
            jmp changeinfo
execs8:     cmp #'J'
            bne execs9
            jmp juggle
execs9:     cmp #'Z'
            bne execs10
            jmp zappedlist
execs10:
            .endif

            cmp #'$'
            bne execs1
            jmp dirlisting
execs1:     cmp #'L'
            beq load
            cmp #'-'
            beq delete
            cmp #'R'
            beq reset
            cmp #'X'
            beq run+1
            cmp #'P'
            beq Program
            cmp #'G'
            beq run
            cmp #'S'
            beq savebounce
            lda commandkey
            beq load

;;;

errbadcmd:  lda #'?'
            .byte $2c
errnostr:
            lda #34
            .byte $2c
errnullstr:
            lda #'0'
            .byte $2c

errnofile:  lda #'X'

error:      .ifndef EPROM
            jsr fullerror
            .endif
            jmp main2


;;; ----------------------------------------------------------------------

reset:      jmp 64802
run:        jmp (ADDR1)

savebounce: beq save

;;; ----------------------------------------------------------------------
;;; .P [N]  N=[0..F]
;;; Loads program at dirindex[N] and jumps to loading address immediately
;;; Defaults to "P 1" if P is entered alone (due to ADDR1 = *43 = 1)

Program:    jsr ResetDirScan
            lda ADDR1
            and #15
            sta DIRINDEX
            stz DIRINDEX+1
            jsr ReadDirectory
            jsr setloadaddr
            lda BUFPTR
            sta ADDR1
            lda BUFPTR+1
            sta ADDR1+1
            jsr loading
            jmp (ADDR1)


;;; ----------------------------------------------------------------------
;;; .- "filename"

delete:     jsr FindName
            bcc delb1
            jmp errnofile

delb1:      stz FILE_TYPE,x
            jmp saveend


;;; ----------------------------------------------------------------------

load:       lda hasname
            beq errnostr
            jsr FindName
            bcs errnofile

            jsr setloadaddr

            ldy hasaddr        ; check if a load addr given
            beq load1

            lda ADDR1
            sta BUFPTR
            lda ADDR1+1
            sta BUFPTR+1

load1:      clc
            lda BUFPTR
            sta 43
            adc FILE_LEN_L,x
            sta 45
            lda BUFPTR+1
            sta 44
            adc FILE_LEN_H,x
            sta 46

            jsr loading

            lda commandkey
            bne load2
            ;jsr $C533
            jsr $C659           ; run BASIC program
            jmp $C7AE

load2:      jmp main


;;; ----------------------------------------------------------------------

save:       lda hasname
            bne saves1
            jmp errnostr

saves1:     lda hasaddr
            cmp #8
            beq saves2

            jsr swapaddrs       ; if no args, these need to be swapped

saves2:     jsr FindName
            bcc save1           ; if name exists, will overwrite
            jsr FindEmpty
            bcc save1           ; if not, find an empty slot

errdiskfull:
            lda #'Z'
            jmp error

save1:      ;; update (in-memory currently) the directory info
            ;; (ADDR1 gets trashed later)
            ;; ADDR1 = ending address
            ;; ADDR2 = load address

            lda ADDR2
            sta BUFPTR
            sta FILE_ADDR_L,x

            lda ADDR2+1
            sta BUFPTR+1
            sta FILE_ADDR_H,x

            sec                 ; ADDR1 -= ADDR2
            lda ADDR1
            sbc BUFPTR
            sta FILE_LEN_L,x
            sta ADDR1
            lda ADDR1+1
            sbc BUFPTR+1
            sta FILE_LEN_H,x
            sta ADDR1+1

            lda #1
            sta FILE_TYPE,x

            ;; copy in the filename (safe to trash X)

            ldy stringstart

save2:      lda LINPUT,y
            sta FILE_NAME,x
            ;; filename length check
            txa
            and #31
            cmp #(FILE_ADDR_L-DIRBUFFER-2)
            bcs save3
            inx
save3:      iny
            cpy stringend
            bcc save2
            beq save2
            stz FILE_NAME,x

            ;; write memory to segment[dirindex]
            ;; it doesn't matter if the entire page is written,
            ;; so no fine boundry checking

            jsr SeekSegment

saveloop:   jsr WriteSector
            inc BUFPTR+1
            inc IDE_LBA0

            dec ADDR1+1
            bpl saveloop        ; VIC20 can't have >32kB RAM..

            ;;

saveend:    jsr UpdateDirectory
            jmp main


;;; ----------------------------------------------------------------------

dirlisting: jsr ResetDirScan

dlistloop:  jsr STOP
            beq dlistexit

            jsr ReadDirectory   ; A/X/Y have the offset..
            lda FILE_TYPE,y
            beq dlistnext
            cmp #255
            beq dlistexit

dlist1:     lda FILE_NAME,x
            jsr CHROUT
            inx
            cmp #0
            bne dlist1

            .ifdef BIGDIRLIST   ; --------------------------------------
dlist4:     lda FILE_TYPE,y     ; handle type=2 'block' files
            cmp #2
            bne dlist8

            lda CURSORPOS
            cmp #21
            bcs dlist7
            lda #20
            sta CURSORPOS
            lda #'B'
            jsr CHROUT
            jmp dlist7

dlist8:     lda CURSORPOS       ; handle plain files
            cmp #12
            bcc dlist6
            lda #17
            .byte $2c
dlist6:     lda #12
            sta CURSORPOS
            lda FILE_ADDR_H,y
            ldx FILE_ADDR_L,y
            jsr HEX16OUT
            lda CURSORPOS
            cmp #17
            bcs dlist7
            inc CURSORPOS
            lda FILE_LEN_H,y
            ldx FILE_LEN_L,y
            jsr HEX16OUT
            .endif              ; --------------------------------------
dlist7:     jsr CROUT

dlistnext:  jsr BumpDirIndex
            bne dlistloop

dlistexit:  jmp main


;;; ----------------------------------------------------------------------
;;; shuffled out for byte-saving

loading:    jsr SeekSegment

loadloop1:  ldx #255
            lda ADDR2+1
            bne loadbr1
            ldx ADDR2
loadbr1:    stx SECTORSIZE
            jsr ReadSector
            inc BUFPTR+1
            inc IDE_LBA0
            dec ADDR2+1
            bpl loadloop1
            ;clc                ; should be cleared after ReadSector
            rts

;;;

swapaddrs:  lda ADDR1           ; trashes Y
            ldy ADDR2
            sta ADDR2
            sty ADDR1
            lda ADDR1+1
            ldy ADDR2+1
            sta ADDR2+1
            sty ADDR1+1
            rts

;;;

setloadaddr:
            lda FILE_ADDR_L,x
            sta BUFPTR
            lda FILE_ADDR_H,x
            sta BUFPTR+1
            lda FILE_LEN_H,x
            sta ADDR2+1
            lda FILE_LEN_L,x
            sta ADDR2
            rts


;;; ----------------------------------------------------------------------

HEX16OUT:   jsr HEX8OUT         ; A=hi byte, X=lo byte
            txa
HEX8OUT:    pha                 ; A=value
            lsr a
            lsr a
            lsr a
            lsr a
            jsr HEX4OUT
            pla
HEX4OUT:    and #15
            ora #48
            cmp #58
            bcc hex1
            adc #6
            .byte $2c
CROUT:      lda #13
hex1:       jmp CHROUT


;;; ----------------------------------------------------------------------
	    .ifdef EPROM
            ;.align 1024
            .endif

            .ifndef EPROM
;;; ----------------------------------------------------------------------
;;; Commands not included in the EPROM to follow
;;; ----------------------------------------------------------------------

;;; ----------------------------------------------------------------------

query:      lda hasname
            beq queryb0
            jsr FindName
            bcc queryprint

queryb0:    lda hasaddr
            beq errnoindex
            jsr maskaddrs
            lda ADDR1
            sta DIRINDEX
            lda ADDR1+1
            sta DIRINDEX+1
            lda CURDIRBLK
            jsr ReadDirectory
            lda CURDIRBLK

            ;; maybe encode unprintable chars?

queryprint: jsr CalcDirOff      ; xxx rename needs this

            lda DIRINDEX+1
            ldx DIRINDEX
            jsr HEX16OUT

            inc CURSORPOS
            lda FILE_TYPE,y
            jsr HEX8OUT
            inc CURSORPOS

            lda FILE_TYPE,y
            cmp #2
            bcc queryb4

            lda #'B'
            jsr CHROUT
            jmp queryb2

queryb4:    lda FILE_ADDR_H,y
            ldx FILE_ADDR_L,y
            jsr HEX16OUT

            inc CURSORPOS
            lda FILE_LEN_H,y
            ldx FILE_LEN_L,y
            jsr HEX16OUT

queryb2:    jsr CROUT
            lda #34
            jsr CHROUT
queryl1:    lda FILE_NAME,y
            beq queryb3
            jsr CHROUT
            iny
            bne queryl1

queryb3:    lda #34
            jsr CHROUT
            jsr CROUT
            jmp main

;;;

errnoindex: lda #'I'
            jmp error

;;; ----------------------------------------------------------------------

changetype: lda hasaddr
            beq errnoindex
            jsr maskaddrs
            jsr CalcDirOff
            lda ADDR1
            sta FILE_TYPE,x
            jsr UpdateDirectory
            jmp queryprint


;;; ----------------------------------------------------------------------

rename:     lda hasname
            bne rens1
            jmp errnostr

rens1:      jsr CalcDirOff
            ldy stringstart
renl1:      lda LINPUT,y
            sta FILE_NAME,x
            inx
            iny
            cpy stringend
            bcc renl1
            beq renl1
            stz FILE_NAME,x
            jsr UpdateDirectory
            jmp queryprint


;;; ----------------------------------------------------------------------

changeinfo: jsr CalcDirOff
            lda hasaddr
            cmp #8
            beq cinfob1
            cmp #4
            beq cinfob2
            lda #'V'
            jmp error

cinfob1:    ;; two values given ADDR2 -> FILE_ADDR, ADDR1 -> FILE_LEN
            jsr swapaddrs
	    lda ADDR2
            sta FILE_LEN_L,x
            lda ADDR2+1
            sta FILE_LEN_H,x

cinfob2:    ;; one value given -- ADDR1 -> FILE_SADDR
            lda ADDR1
            sta FILE_ADDR_L,x
            lda ADDR1+1
            sta FILE_ADDR_H,x

            jsr UpdateDirectory
            jmp queryprint


;;; ----------------------------------------------------------------------

            ;; 0 0000 0000
            ;; 1 0000 000x
            ;; 2 0000 00xx
            ;; 3 0000 0xxx
            ;; 4 0000 xxxx
            ;; 5 000x xxxx
            ;; 6 00xx xxxx
            ;; 7 0xxx xxxx
            ;; 8 xxxx xxxx
            ;;
            ;; mask = 16^n - 1
            ;; mask = 2^4n - 1 ...

            .ifndef FOO1

maskaddrs:  ldy hasaddr         ; 7 bytes more code, but obviously
            lda ADDR2+1         ; a lot faster
            cpy #8
            bcs mask1
            and #15
mask1:      cpy #7
            bcs mask6
            lda #0
mask6:      sta ADDR2+1

            lda ADDR2
            cpy #6
            bcs mask2
            and #15
mask2:      cpy #5
            bcs mask7
            lda #0
mask7:      sta ADDR2

            lda ADDR1+1
            cpy #4
            bcs mask3
            and #15
mask3:      cpy #3
            bcs mask8
            lda #0
mask8:      sta ADDR1+1

            lda ADDR1
            cpy #2
            bcs mask4
            and #15
mask4:      cpy #0
            bne mask5
            lda #0
mask5:      sta ADDR1
            rts

            .endif
            .ifdef FOO1

maskaddrs:  lda #0
            sta maskbits
            sta maskbits+1
            sta maskbits+2
            sta maskbits+3
            ldy hasaddr
maskloop1:  cpy #0
            beq maskout
            ldx #4
maskloop2:  sec
            rol maskbits
            rol maskbits+1
            rol maskbits+2
            rol maskbits+3
            dex
            bne maskloop2
            dey
            bne maskloop1
maskloop3:  lda ADDR1,y
            and maskbits,y
            sta ADDR1,y
            iny
            cpy #4
            bne maskloop3
maskout:    rts

maskbits:   .word 0,0     ; okay to do as 'extended' 6522IDE is in RAM

            .endif


;;; ----------------------------------------------------------------------

zappedlist: jsr ResetDirScan

zaploop1:   jsr STOP
            beq zapexit

            jsr ReadDirectory

            ldy #255
            lda FILE_TYPE,x
            cmp #255
            beq zapprint
            cmp #0              ; if it's any kind of file, skip
            bne zapnext

zaploop2:   iny
            lda FILE_NAME,x
            bne zapprint
            inx
            cpy #31
            bne zaploop2

zapnext:    jsr BumpDirIndex
            lda DIRINDEX+1
            cmp #4
            bne zaploop1
zapexit:    jmp main

zapprint:   lda DIRINDEX+1
            ldx DIRINDEX
            jsr HEX16OUT
            inc CURSORPOS
            cpy #255
            bne zap2

            lda #'E'
            jsr CHROUT
            lda #'O'
            jsr CHROUT
            lda #'D'
            jsr CHROUT
zap3:       jsr CROUT
            jmp zapnext

zap2:       jsr CalcDirOff
            jsr printnamex
            beq zap3

;;;

printnamex: lda FILE_NAME,x
            jsr CHROUT
            inx
            cmp #0
            bne printnamex
            rts


;;; ----------------------------------------------------------------------

juggle:     lda hasaddr
            bne juggle3
            jmp errnoindex

juggle3:    jsr maskaddrs
            jsr FindName
            bcc juggle2
            jmp errnofile

            ;; read named record

juggle2:    lda DIRINDEX
            sta file1
            lda DIRINDEX+1
            sta file1+1

            ;; copy to juggrecord (swaps without updating)

            lda FILE_LEN_H,x
            sta filelen

            jsr juggleswap

            ;; read indexed record

            lda ADDR1
            sta DIRINDEX
            lda ADDR1+1
            sta DIRINDEX+1

            lda #255           ; invalidate cache if in the same block
            sta CURDIRBLK
            jsr ReadDirectory

            ;; swap dirbuffer,x and juggrecord
            ;; updatedir

            lda FILE_LEN_H,x
            cmp filelen
            bmi juggle4
            sta filelen

juggle4:    jsr juggleswap
            jsr UpdateDirectory

            ;; read named record back

            lda file1
            sta DIRINDEX
            lda file1+1
            sta DIRINDEX+1

            jsr ReadDirectory

            ;; swap dirbuffer,x and juggrecord
            ;; updatedir

            jsr juggleswap
            jsr UpdateDirectory

            ;; now to swap the segments

            lda #255   ; needed when mixing directory and sector reads..
            sta SECTORSIZE

            lda #<RAMBUF2
            sta BUFPTR
            lda #>RAMBUF2+1
            sta BUFPTR+1


juggle6:    lda filelen         ; read from file1
            sta IDE_LBA0
            lda file1
            sta IDE_LBA8
            lda file1+1
            sta IDE_LBA16
            dec BUFPTR+1        ; lda #>RAMBUF1+1 ; sta BUFPTR+1
            jsr ReadSector

            lda ADDR1           ; read from file2
            sta IDE_LBA8
            lda ADDR1+1
            sta IDE_LBA16
            inc BUFPTR+1        ; lda #>RAMBUF2+1 ; sta BUFPTR+1
            jsr ReadSector

            dec BUFPTR+1        ; lda #>RAMBUF1+1 ; sta BUFPTR+1
            jsr WriteSector     ; write file1 content to file2

            lda file1           ; write file2 content to file1
            sta IDE_LBA8
            lda file1+1
            sta IDE_LBA16
            inc BUFPTR+1        ; lda #>RAMBUF2+1 ; sta BUFPTR+1
            jsr WriteSector

            dec filelen
            lda filelen
            cmp #255
            bne juggle6

            jmp main

;;;

juggleswap: ldy #0
juggswap1:  lda DIRBUFFER,x
            pha
            lda juggtemp,y
            sta DIRBUFFER,x
            pla
            sta juggtemp,y
            inx
            iny
            cpy #32
            bne juggswap1
            rts

;;;

file1:      .word 0             ; dirindex backup
filelen:    .byte 0             ; in blocks

juggtemp:   .res 32, $01

RAMBUF1:    .res 256, $02       ; keep these two together
RAMBUF2:    .res 256, $03


;;; ----------------------------------------------------------------------

fullerror:  sta errorsave
            ldx #0

ferr1:      lda errtxt,x
            beq ferrexit
            eor errorsave
            sta errordo
            inx
ferr2:      inx
            lda errtxt-1,x
            beq ferr1
            eor errordo
            cmp errtxt-1,x
            bne ferr4
            jsr CHROUT
ferr4:      jmp ferr2
ferrexit:   lda #0
            rts

;;;

errorsave:  .byte 0
errordo:    .byte 0

errtxt:     .byte '?', "NO COMMAND", 0
            .byte  34, "NO FILENAME", 0
            .byte '0', "EMPTY FILENAME", 0
            .byte 'X', "UNKNOWN FILENAME", 0
            .byte 'I', "NO INDEX", 0
            .byte 'V', "BAD VALUES", 0
            .byte 'Z', "DISK FULL", 0
            .byte '!', "6522IDE DOS V1.0 20170328 "
            .byte      "CJB<AT>BRUSHTAIL.APANA.ORG.AU", 0
            .byte 0

;;; ----------------------------------------------------------------------

;;; ----------------------------------------------------------------------
	    .endif              ; ifndef EPROM
;;; ----------------------------------------------------------------------
